﻿using System;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Threading;
using NSubstitute;
using Phoenix.ActionResults;
using Phoenix.Diagnostics;
using Phoenix.Filters;
using Phoenix.Filters.ActionFilters;
using Phoenix.Frames;
using Phoenix.Framework;
using Xunit;

namespace Phoenix.Tests.Framework
{
    public class ActionExecutionTests
    {
        readonly ControllerActionContext currentContext;
        readonly InvokeActionRequest request;
        readonly ActionExecutingTestController controller;
        readonly ActionExecution subject;
        readonly IControllerFactory defaultControllerFactory;

        public ActionExecutionTests()
        {
            currentContext = PhoenixObjectMother.CreateContext(ControllerAction.CreateFrom("Controller.CurrentAction"));
            var controllerAction = ControllerAction.CreateFrom("Controller.FilteredAction");
            var controllerActionParameters = Enumerable.Empty<ControllerActionParameter>();
            request = new InvokeActionRequest(currentContext, controllerAction, controllerActionParameters, InvokeOptions.None)
                {
                    TargetFrame = Substitute.For<IContentFrame>()
                };
            var phoenixHost = Substitute.For<IPhoenixHost>();
            phoenixHost.AppScheduler.Returns(TaskScheduler.Default);
            var controllerLocator = Substitute.For<IControllerLocator>();
            var returnThis = typeof (ActionExecutingTestController);
            controllerLocator.Locate(Arg.Any<ControllerAction>()).Returns(returnThis);
            controller = new ActionExecutingTestController();
            defaultControllerFactory = Substitute.For<IControllerFactory>();
            defaultControllerFactory
                .Create(returnThis, Arg.Any<ControllerActionContext>())
                .Returns(controller);

            subject = new ActionExecution(request,
                                          PhoenixObjectMother.CreateContext(controllerAction),
                                          Substitute.For<IErrorReporter>(),
                                          phoenixHost,
                                          controllerLocator,
                                          defaultControllerFactory);
        }

        [Fact]
        public void when_actionfilter_sets_result_it_is_used_instead_of_executing_controller_action()
        {
            // arrange
            //Need nullable type to make sure that the controller instance we are testing is the one that is executed
            controller.FilteredActionExecuted = false;

            // act
            subject.Execute(Dispatcher.CurrentDispatcher, TaskScheduler.Default);
            subject.ExecutionTask.Wait();

            // assert
            Assert.False(controller.FilteredActionExecuted.Value);
        }

        public class ActionExecutingTestController : Controller
        {
            [SomeActionFilter]
            public ActionResult FilteredAction()
            {
                FilteredActionExecuted = true;
                return Nothing();
            }

            public bool? FilteredActionExecuted { get; set; }
        }

        public class SomeActionFilterAttribute : FilterAttribute, IActionFilter
        {
            public void OnActionExecuting(ActionExecutingContext filterContext)
            {
                filterContext.Result = new DoNothingActionResult(filterContext.ActionContext);
            }

            public void OnActionExecuted(ActionExecutedContext filterContext)
            {
            }
        }
    }
}